package com.personalkeeper;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

/*
 * Receives notifications from GPSPollingService. Bound to the thread
 * that is creating it.
 */
public class LocationServiceController {


    private static final String TAG = LocationServiceController.class.toString();
    private static void log(String msg) {
        Log.d(TAG, msg);
    }

    public interface LocationListener {
        void onLocationUpdate(Location location);
    }

    public interface SMSListener {
        void onSMSReceived(Bundle smsInfo);
    }

    public interface ConnectionStateListener {
        void onServiceDisconnected();
        void onServiceConnected();
    }

    class LocationUpdateHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case GPSPollingService.MSG_LOCATION_CHANGED:
                try {
                    Location location = (Location) msg.obj;
                    if (mLocationListener != null)
                        mLocationListener.onLocationUpdate(location);
                } catch (ClassCastException e) {
                    log("msg.obj is not Location object");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;

            case GPSPollingService.MSG_SMS_RECEIVED:
                if (mSMSListener != null) {
                    try {

                        mSMSListener.onSMSReceived((Bundle) msg.obj);
                    } catch (ClassCastException e) {
                        log("msg.obj is not Bundle object");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                break;

            default:
                break;
            }
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName name, IBinder service) {
            log("onServiceConnected()");
            mService = new Messenger(service);
            try {
                startLocationUpdates(mService, mMessageReceiver);
            } catch (RemoteException e) {
                log("can't register self");
            }
            if (mConectionStateListener != null)
                try {
                    mConectionStateListener.onServiceConnected();
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            log("onServiceDisconnected()");
            mService = null;
            if (mConectionStateListener != null)
                try {
                    mConectionStateListener.onServiceDisconnected();
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    };
    private LocationListener mLocationListener;
    private SMSListener mSMSListener;
    private ConnectionStateListener mConectionStateListener;
    private Context mContext;
    private Messenger mService;
    private Messenger mMessageReceiver = new Messenger(new LocationUpdateHandler());

    public LocationServiceController(Context context) {
        mContext = context;
    }

    public void setLocationListener(LocationListener listener) {
        mLocationListener = listener;
    }

    public void setSMSListener(SMSListener listener) {
        mSMSListener = listener;
    }

    public void setConnectionStateListener(ConnectionStateListener listener) {
        mConectionStateListener = listener;
    }

    public void rereadConfiguration() {
        try {  // arg1 argument is not used
            sendMessageWithArgument(mService, GPSPollingService.MSG_CONFIGURATION_CHANGED, 0);
        } catch (RemoteException e) {
        }
    }

    public void startService() {
        Intent intent = new Intent(mContext, GPSPollingService.class);
        mContext.startService(intent);
    }

    public void stopService() {
        mContext.stopService(new Intent(mContext, GPSPollingService.class));
    }

    public boolean isBound() {
        return mService != null;
    }

    public boolean bindService() {
        if (!isBound()) {
            return mContext.bindService(
                       new Intent(mContext, GPSPollingService.class),
                       mConnection,
                       Context.BIND_AUTO_CREATE);
        }
        return true;
    }

    public void unbindService() {
        if (isBound()) {
            try {
                stopLocationUpdates(mService, mMessageReceiver);
            } catch (RemoteException e) {
                log("service is dead, can't unregister self");
            }
            mService = null;
        }
        try {
            mContext.unbindService(mConnection);
        } catch (IllegalStateException e) {
            e.printStackTrace(); // sometimes happens if service was killed few times
        }
    }

    private static void sendMessageWithArgument(Messenger service, int what, int arg1)
            throws RemoteException {
        if (service != null) {
            Message startLocationUpdates = Message.obtain();
            startLocationUpdates.what = what;
            startLocationUpdates.arg1 = arg1;

            service.send(startLocationUpdates);
        } else
            throw new IllegalStateException("service is unbound");
    }

    private static void manageUpdates(Messenger service, int what, Messenger replyTo)
            throws RemoteException {
        if (service != null) {
            Message startUpdates = Message.obtain();
            startUpdates.what = what;
            startUpdates.replyTo = replyTo;

            service.send(startUpdates);
        } else
            throw new IllegalStateException("service is unbound");
    }

    private static void startLocationUpdates(Messenger service, Messenger updateReciever)
    throws RemoteException {
        manageUpdates(service, GPSPollingService.MSG_REGISTER_CLIENT, updateReciever);
    }

    private static void stopLocationUpdates(Messenger service, Messenger updateReciever)
    throws RemoteException {
        manageUpdates(service, GPSPollingService.MSG_UNREGISTER_CLIENT, updateReciever);
    }
}
